/*
    This file is part of Ecosim.

    Ecosim is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 3 of the License, or
    (at your option) any later version.

    Ecosim is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see http://www.gnu.org/licenses/.
 */
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.Diagnostics;

namespace EcosimBase
{
    public class Creature : IDisposable
    {
        private object syncObj = null;

        #region event declarations
        public event HitWallHandler HitWallEvent;
        public event MovedHandler MovedEvent;
        #endregion

        public enum Direction
        {
            Forward,
            Backward,
            Left,
            Right
        }

        private CreatureData data;
        public CreatureData Data
        {
            get { return data; }
        }

        protected Thread behaviorThread;

        protected bool running = false;

        #region baseMethods
        public Creature(int startX, int startY)
        {
            this.data = new CreatureData(10, startX, startY);
            this.behaviorThread = new Thread(new ThreadStart(ThreadRun));
            this.HitWallEvent += new HitWallHandler(Creature_HitWallEvent);
            this.MovedEvent += new MovedHandler(Creature_MovedEvent);
            this.syncObj = new object();
        }

        ~Creature()
        {
            this.Dispose();
        }

        public void Start()
        {
            this.running = true;
            this.behaviorThread.Start();
        }
        
        public void Dispose()
        {
            this.running = false;
        }

        private void ThreadRun()
        {
            while (this.running)
            {
                if (this.data.Health <= 0)
                {
                    this.behaviorThread.Abort();
                }
                    this.Run();
            }
            if (!this.running)
            {
                this.behaviorThread.Abort();
            }
        }
        #endregion

        #region Overrideable methods
        protected virtual void Run()
        {
        }
        #endregion

        #region Creature actions
        public void move(Direction direction, int distance)
        {
            if (direction == Direction.Forward)
            {
                for (int i = 1; i <= distance; i++)
                {
                    if (this.data.Y + 1 == Field.Height)
                    {
                        HitWallEvent(new HitWallEventArgs(Direction.Forward));          
                        break;
                    }
                    lock (this.syncObj)
                    {
                        this.data = new CreatureData(this.data.Health, this.data.X, this.data.Y + 1);
                    }
                    MovedEvent(new MovedEventArgs());
                }
            }
            else if (direction == Direction.Backward)
            {
                for (int i = 1; i <= distance; i++)
                {
                    if (this.data.Y == 0)
                    {
                        HitWallEvent(new HitWallEventArgs(Direction.Backward));
                        break;
                    }
                    lock (this.syncObj)
                    {
                        this.data = new CreatureData(this.data.Health, this.data.X, this.data.Y - 1);
                    }
                    MovedEvent(new MovedEventArgs());
                }
            }
            else if (direction == Direction.Left)
            {
                for (int i = 1; i <= distance; i++)
                {
                    if (this.data.X == 0)
                    {
                        HitWallEvent(new HitWallEventArgs(Direction.Left));
                        break;
                    }
                    lock (this.syncObj)
                    {
                        this.data = new CreatureData(this.data.Health, this.data.X, this.data.X - 1);
                    }
                    MovedEvent(new MovedEventArgs());
                }
            }
            else if (direction == Direction.Right)
            {
                for (int i = 1; i <= distance; i++)
                {
                    if (this.data.X + 1 == Field.Width)
                    {
                        HitWallEvent(new HitWallEventArgs(Direction.Right));
                        break;
                    }
                    lock (this.syncObj)
                    {
                        this.data = new CreatureData(this.data.Health, this.data.X, this.data.X + 1);
                    }
                    MovedEvent(new MovedEventArgs());
                }
            }
            System.Threading.Thread.Sleep(100);
        }

        public void tryEat(Plant p)
        {
            if (p.X == this.data.X && p.Y == this.data.Y)
            {
                lock (this.syncObj)
                {
                    this.data = new CreatureData(this.data.Health + p.Size, this.data.X, this.data.Y);
                }
                p.Dispose();
            }
        }
        #endregion

        #region event methods

        protected virtual void Creature_HitWallEvent(HitWallEventArgs e)
        {

        }

        protected virtual void Creature_MovedEvent(MovedEventArgs e)
        {
        }

        #endregion
    }

    public delegate void HitWallHandler(HitWallEventArgs e);
    public class HitWallEventArgs : EventArgs
    {
        private Creature.Direction wall;
        public Creature.Direction Wall
        {
            get { return wall; }
            set { wall = value; }
        }

        public HitWallEventArgs(Creature.Direction wall)
        {
            this.wall = wall;
        }
    }

    public delegate void MovedHandler(MovedEventArgs e);
    public class MovedEventArgs : EventArgs { }
}
